var fs = require('fs')
var thunky = require('thunky')
var low = require('last-one-wins')
var alloc = require('buffer-alloc-unsafe')
var c = fs.constants || require('constants')
var events = require('events')
var util = require('util')

function RAF (name, opts) {
  if (!(this instanceof RAF)) return new RAF(name, opts)
  if (!opts) opts = {}

  events.EventEmitter.call(this)

  var self = this

  this.fd = 0
  this.length = 0
  this.readable = !!opts.readable
  this.writable = !!opts.writable
  this.filename = name
  this.directory = opts.directory

  this.opened = false
  this.closed = false
  this.open = thunky(open)

  this._close = low(close)
  this._closing = false
  this._pending = 0
  this._onidleclose = null

  function close (_, cb) {
    self._doClose(cb)
  }

  function open (cb) {
    self._open(cb)
  }
}

util.inherits(RAF, events.EventEmitter)

RAF.prototype._open = function (cb) {
  if (this._closing) return cb(new Error('File is closed'))
  var self = this

  this._pending++
  fs.open(this.filename, c.O_RDWR | c.O_CREAT, function (err, fd) {
    if (err) return self._done(cb, err)
    self.opened = true
    self.fd = fd
    self.emit('open')
    self._done(cb, null)
  })
}

RAF.prototype.write = function (offset, data, cb) {
  if (this._closing) return cb(new Error('File is closed'))
  if (!cb) cb = noop

  var self = this

  this._pending++
  this.open(function (err) {
    if (err) return self._done(cb, err)
    self._write(offset, data, cb)
  })
}

RAF.prototype._write = function (offset, data, cb) {
  var self = this
  var length = data.length

  fs.write(this.fd, data, 0, length, offset, onwrite)

  function onwrite (err, wrote) {
    if (err) return self._done(cb, err)

    length -= wrote
    offset += wrote

    if (length) return fs.write(self.fd, data, data.length - length, length, offset, onwrite)

    self._done(cb, null)
  }
}

RAF.prototype.read = function (offset, length, cb) {
  if (this._closing) return cb(new Error('File is closed'))

  var self = this

  this._pending++
  this.open(function(err) {
    if (err) return self._done(cb, err)
    self._read(offset, length, cb)
  })
}

RAF.prototype._read = function (offset, len, cb) {
  var self = this
  var data = Buffer.isBuffer(len) ? len : alloc(len)
  var length = data.length

  fs.read(this.fd, data, 0, length, offset, onread)

  function onread (err, read) {
    if (err) return self._done(cb, err)

    length -= read
    offset += read

    if (length) return fs.read(self.fd, data, data.length - length, length, offset, onread)

    self._done(cb, null, data)
  }
}

RAF.prototype._done = function (cb, err, val) {
  this._pending--

  if (this._pending === 0) {
    if (this._onidleclose) this._doClose(this._onidleclose)
  }

  cb(err, val)
}

RAF.prototype.close = function (cb) {
  this._closing = true
  this._close(null, cb)
}

RAF.prototype.destroy = function (cb) {
  if (!cb) cb = noop

  this.close(function(err) {
    if (err) return cb(err)
    console.log('lets destroy')
  })
}

RAF.prototype._doClose = function (cb) {
  this._onidleclose = null

  if (this._pending) {
    this._onidleclose = cb
    return
  }

  if (!this.fd) {
    process.nextTick(cb)
    return
  }

  var self = this

  fs.close(this.fd, onclose)

  function onclose (err) {
    if (err) return cb(err)
    self.fd = 0
    self.emit('close')
    cb(null)
  }
}

// RAF.prototype.destroy = function (cb) {
//   if (!cb) cb = noop

//   var self = this

//   this.close(function (err) {
//     if (err) return cb(err)

//     self._ondestroy = self._ondestroy ? combine(self._ondestroy, cb) : cb

//     if (self.destroyed) return
//     self.destroyed = true

//     fs.unlink(self.filename, function (err) {

//     })

//     console.log('should destroy')
//   })
// }

// RAF.prototype.close = function (cb) {
//   if (!cb) cb = noop

//   if (!this._opening) {
//     this.closed = true
//     process.nextTick(cb)
//     return
//   }

//   this._onclose = this._onclose ? combine(this._onclose, cb) : cb
//   if (this.closed) return
//   this.closed = true

//   if (!this._pending) self._doClose()
// }

// RAF.prototype._doClose = function () {
//   var self = this

//   if (this.fd) fs.close(this.fd, done)
//   else done()

//   function done (err) {
//     var onclose = self._onclose
//     self._onclose = null
//     self._opening = false
//     self.fd = 0
//     if (onclose) onclose(err)
//   }
// }

// function combine (a, b) {
//   return function (err) {
//     a(err)
//     b(err)
//   }
// }

function mode (self) {
  if (self.readable && self.writable) return c.O_RDWR | c.O_CREAT
  if (self.writable) return c.O_WRONLY | c.O_CREAT
  return c.O_RDONLY
}

function noop () {}

var f = RAF('/tmp/test')

f.on('open', function () {
  console.log('opened')
})
f.on('close', function () {
  console.log('closed')
})

f.write(0, new Buffer('hi'), function () {
  f.read(0, 2, console.log)
  f.close(console.log)
  f.close(console.log)
  f.close(console.log)
})
